#define _GNU_SOURCE
#include <stdio.h>
#include <stdint.h>
#include <time.h>
#include <sched.h>
#include <pthread.h>
#include <stdlib.h>
#include <errno.h>
#include <assert.h>
#include "hash.h"


#define __pl()  printf("line = %d\n", __LINE__)
//#define __pl()

#define TEST_SEC	60
#define NR_THR		32
#define NR_ENTRIES	(3000*10000UL)
#define BATCH_NUM	10000

#define CCR_MULTIPLY_64           6364136223846793005
#define CCR_ADD_64                1

#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)


/*
 * 每线程一个，用于记录操作次数等信息
 */
struct stat_t {
	int      id;           // 这个线程id，也就是序号
	uint32_t pad0;
	pthread_t pth;          // 这个线程的ID
	uint64_t nr_key;        // 本线程访问的key的最大范围
	MyHashMap *hash;
	uint64_t nr;            // 测试操作次数
	uint64_t rand_seed;     // 随机数的种子
	uint64_t nr_add, nr_del, nr_lkup;
	uint64_t pad1[7];
}__attribute__((aligned(64)));

static struct stat_t g_stat[NR_THR];

//线程绑定CPU核
void set_affinity(int nr_cpu)
{
        pthread_t thread;
        cpu_set_t cpuset;
        int i, ret;

        thread = pthread_self();

        CPU_ZERO(&cpuset);
	for (i = 0; i < nr_cpu; i++) {
		CPU_SET(i, &cpuset);
	}

        ret = pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpuset);
        if (ret != 0) {
                fprintf(stderr, "pthread_setaffinity_np(): failed, errno=%d, ret=%d\n", errno, ret);
                exit(1);
        }
}

static uint64_t get_ns(void)
{
	struct timespec val;
	uint64_t v;
	int ret;

	ret = clock_gettime(CLOCK_REALTIME, &val);
	if (ret != 0) {
		perror("clock_gettime");
		exit(1);
	}
	v  = (uint64_t) val.tv_sec * 1000000000LL;
	v += (uint64_t) val.tv_nsec;
	return v;
}

static inline uint64_t my_rand64(uint64_t *r)
{
	*r = *r * CCR_MULTIPLY_64 + CCR_ADD_64;
	return *r;
}

static void do_test(struct stat_t *s)
{
	uint64_t key, value;

	value = my_rand64(&s->rand_seed);
	if (unlikely(value%10 == 0)) {
		if (s->nr_del >= s->nr_add || (value & 2) == 0) {
			value = s->nr_add;
			key = my_rand64(&value);
			myHashMapPut(s->hash, key, value);
			s->nr_add++;
		} else {
			value = s->nr_del;
			key = my_rand64(&value);
			myHashMapRemove(s->hash, key);
			s->nr_del++;
		}
		return;
	}

	key = value % s->nr_key;
	my_rand64(&key);
	value = myHashMapGet(s->hash, key);
	(void)value;
	s->nr_lkup++;
}

void *test_thr(void *arg)
{
	struct stat_t *s = (struct stat_t *)arg;
	int sec = 0;
	uint64_t t0, t1;
	uint64_t i;

	set_affinity(1);
	t0 = get_ns();
	t1 = t0;
	while (1) {
		s->rand_seed = t1;
		for (i = 0; i < BATCH_NUM; i++) {
			do_test(s);
		}
		s->nr += BATCH_NUM;

		t1 = get_ns();
		if (likely(t1 - t0 < 1000000000UL))
			continue;

		t0 = t1;
		sec ++;
		if (sec == 1) {
			set_affinity(16);
		} else if (sec == 2) {
			set_affinity(32);
		} else if (sec >= 10) {
			break;
		}
	}
	return NULL;
}

int main()
{
	int ret;
	uint64_t i, sum = 0;
	uint64_t key, value;
	MyHashMap *hash;

	assert(sizeof(struct stat_t) == 128);

	hash = myHashMapCreate(NR_ENTRIES);
	assert(hash);

	printf("adding keys\n");
	for (i = 0; i <3000*1000UL; i++) {
		key = i;
		my_rand64(&key);
		value = key;
		my_rand64(&value);
		myHashMapPut(hash, key, value);
	}

	printf("start pthread\n");

	for (i = 0; i < NR_THR; i++) {
		if (i < 1)
			g_stat[i].nr_key = 1000;
		else if (i < 2)
			g_stat[i].nr_key = 32768;
		else if (i < 12)
			g_stat[i].nr_key = 120*10000UL;
		else if (i < 32)
			g_stat[i].nr_key = 3000*10000UL;
		else
			assert(0);

		g_stat[i].hash = hash;
		ret = pthread_create(&g_stat[i].pth, NULL, test_thr, &g_stat[i]);
		assert(ret == 0);
	}

	for (i = 0; i < NR_THR; i++) {
		ret = pthread_join(g_stat[i].pth, NULL);
		assert(ret == 0);
	}
	for (i = 0; i < NR_THR; i++)
		sum += g_stat[i].nr;

	printf("%lu\n", sum);
	for (i = 0; i < NR_THR; i++)
		printf("thr_%02lu: nr=%10lu, nr_key=%10lu, "\
			"nr_add=%10lu, nr_del=%10lu, nr_lkup=%10lu\n",
			i,
			g_stat[i].nr,
			g_stat[i].nr_key,
			g_stat[i].nr_add,
			g_stat[i].nr_del,
			g_stat[i].nr_lkup
			);
	return 0;
}


